/**
@file Server.cpp

Includes implementation for main Physics Engine class

@see Physics::CServer

@author Carlos Fraguas
@date December, 2013
*/

#include "Server.h"
#include "Entity.h"

#include "Logic/Entity/Entity.h"

#include <assert.h>

namespace Physics {
	CServer *CServer::_instance = 0;

	CServer::CServer() {
		assert(!_instance && "Second initialization of Physics::CServer not allowed!");

		_instance = this;
	}

	CServer::~CServer() {
		assert(_instance && "Can't destroy Physics::CServer as it was not initialized!");
		_instance = 0;
	}

	bool CServer::Init() {
		assert(!_instance && "Second initialization of Physics::CServer not allowed!");

		new CServer();

		return true;
	}

	void CServer::Release() {
		if(_instance) {
			_instance->releasePhysicsScene();
			delete _instance;
		}
	}

	bool CServer::initPhysicsScene() {
		if(_scene.size() > 0 || _triggers.size() > 0) {
			releasePhysicsScene();
		}

		// perform further initialization
		return true;
	}

	bool CServer::releasePhysicsScene() {
		TScene::const_iterator it;
		// remove every entity in the scene
		for(it = _scene.begin(); it != _scene.end(); ++it) {
			_scene.erase(it);
		}
		// remove every trigger in the scene
		for(it = _triggers.begin(); it != _triggers.end(); ++it) {
			_triggers.erase(it);
		}

		// perform further release
		return true;
	}

	void CServer::tick(float secs) {
		// update all physics entities
		TScene::const_iterator it;
		for(it = _scene.begin(); it != _scene.end(); ++it) {
			(*it)->tick(secs);
		}
		// update all triggers in case they need to do something
		for(it = _triggers.begin(); it != _triggers.end(); ++it) {
			(*it)->tick(secs);
		}

		// do nothing else if scene is empty
		// not checking triggers cause they are passive
		if(_scene.empty()) {
			return;
		}

		// check collisions between physics entities
		checkCollisions();
		// check for entities entering triggers
		checkTriggersEnter();
	}

	void CServer::checkCollisions() const {
		TScene::const_iterator itCollider, itCollided;
		// collisions for elements
		for(itCollider = _scene.begin() + 1; itCollider != _scene.end(); ++itCollider) {
			// for each element, check collisions with every element prior to it in scene
			for(itCollided = _scene.begin(); itCollided != itCollider; ++itCollided) {
				// check collision between elements
				if((*itCollider)->collidesWith(*itCollided)) {
					// they've collided, so we are notifying both of them
					Logic::TMessage m1;
					m1._type = Logic::TMessageType::COLLISION;
					m1._collider = (*itCollided)->_logicEntity;

					Logic::TMessage m2;
					m2._type = Logic::TMessageType::COLLISION;
					m2._collider = (*itCollider)->_logicEntity;

					// emit both messages
					(*itCollider)->_logicEntity->emitMessage(m1);
					(*itCollided)->_logicEntity->emitMessage(m2);
				}
			}
		}
	}

	void CServer::checkTriggersEnter() const {
		TScene::const_iterator itCollider, itTrigger;
		// "collisions" with triggers
		for(itCollider = _scene.begin(); itCollider != _scene.end(); ++itCollider) {
			// for each element check if it collided with a trigger
			for(itTrigger = _triggers.begin(); itTrigger != _triggers.end(); ++itTrigger) {
				// check collisions
				if((*itCollider)->collidesWith(*itTrigger)) {
					// the entity entered inside the trigger, so deliver a message
					Logic::TMessage m;
					m._type = Logic::TMessageType::TRIGGER_ENTER;
					m._collider = (*itCollider)->_logicEntity;

					// emit message, only to trigger
					(*itTrigger)->_logicEntity->emitMessage(m);
				}
			}
		}
	}

	void CServer::addEntity(CEntity *entity, bool isTrigger) {
		if(isTrigger) {
			_triggers.push_back(entity);
		} else {
			_scene.push_back(entity);
		}
	}

	void CServer::removeEntity(CEntity *entity) {
		TScene::const_iterator it;
		// remove it from scene
		for(it = _scene.begin(); it != _scene.end(); ++it) {
			if((*it) == entity) {
				_scene.erase(it);
				return;
			}
		}

		// if we're here then it's not found (mind that return statement)
		// in that case, it must be a trigger
		for(it = _triggers.begin(); it != _triggers.end(); ++it) {
			if((*it) == entity) {
				_triggers.erase(it);
				return;
			}
		}
	}
}